/////////////////////////////////////////////////////////////////////////////
// Name:        ax_textctrl.cpp
// Purpose:     Accessibility classes: AxTextCtrl (speaking text control)
// Author:      Julian Smart
// Modified by:
// Created:     2009-03-20
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"
#include "wx/srchctrl.h"

#include "ax/ax_textctrl.h"
#include "ax/ax_speech.h"
#include "ax/ax_utils.h"
#include "ax/ax_view.h"
#include "shortcutcust/shortcutcust.h"

IMPLEMENT_DYNAMIC_CLASS( AxTextCtrl, wxTextCtrl )

AxTextCtrl::~AxTextCtrl()
{
    if (m_textEventHandler && GetEventHandler() == m_textEventHandler)
    {
        m_textEventHandler->StopSpeech();
        PopEventHandler(true);
    }
}

bool AxTextCtrl::Create(wxWindow* parent, wxWindowID id, const wxString& value,
            const wxPoint& pos, const wxSize& size, int style)
{
    wxTextCtrl::Create(parent, id, value, pos, size, style);

    m_textEventHandler = new AxTextCtrlAdapter(this);
    AxSelfVoicing::AddAdapter(this, m_textEventHandler);

    m_textEventHandler->UpdateAppearance(AxVisualSettingAll);

    return true;
}

void AxTextCtrl::Init()
{
    m_textEventHandler = NULL;
}

void AxTextCtrl::SetSpeech(AxSpeech* speech)
{
    if (m_textEventHandler)
        m_textEventHandler->SetSpeech(speech);
}

AxSpeech* AxTextCtrl::GetSpeech() const
{
    if (m_textEventHandler)
        return m_textEventHandler->GetSpeech();
    else
        return NULL;
}

void AxTextCtrl::SetShortcutManager(wxShortcutManager* shortcutManager)
{
    if (m_textEventHandler)
        m_textEventHandler->SetShortcutManager(shortcutManager);
}

wxShortcutManager* AxTextCtrl::GetShortcutManager() const
{
    if (m_textEventHandler)
        return m_textEventHandler->GetShortcutManager();
    else
        return NULL;
}

IMPLEMENT_DYNAMIC_CLASS(AxTextCtrlAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxTextCtrlAdapter, AxAdapter)
    EVT_MENU(AxGetId(AxIdentifierReadCurrentWord),      AxTextCtrlAdapter::OnReadCurrentWord)
    EVT_MENU(AxGetId(AxIdentifierReadNextWord),         AxTextCtrlAdapter::OnReadNextWord)
    EVT_MENU(AxGetId(AxIdentifierReadPreviousWord),     AxTextCtrlAdapter::OnReadPreviousWord)

    EVT_MENU(AxGetId(AxIdentifierReadCurrentSentence),  AxTextCtrlAdapter::OnReadCurrentSentence)
    EVT_MENU(AxGetId(AxIdentifierReadNextSentence),     AxTextCtrlAdapter::OnReadNextSentence)
    EVT_MENU(AxGetId(AxIdentifierReadPreviousSentence), AxTextCtrlAdapter::OnReadPreviousSentence)

    EVT_MENU(AxGetId(AxIdentifierReadCurrentParagraph), AxTextCtrlAdapter::OnReadCurrentParagraph)
    EVT_MENU(AxGetId(AxIdentifierReadNextParagraph),    AxTextCtrlAdapter::OnReadNextParagraph)
    EVT_MENU(AxGetId(AxIdentifierReadPreviousParagraph), AxTextCtrlAdapter::OnReadPreviousParagraph)

    EVT_MENU(AxGetId(AxIdentifierReadFromHere),         AxTextCtrlAdapter::OnReadFromHere)
    EVT_MENU(AxGetId(AxIdentifierReadAll),              AxTextCtrlAdapter::OnReadAll)

    EVT_MENU(AxGetId(AxIdentifierDescribeContext),      AxTextCtrlAdapter::OnDescribeContext)
    EVT_MENU(AxGetId(AxIdentifierStopSpeech),           AxTextCtrlAdapter::OnStopSpeech)
    EVT_MENU(AxGetId(AxIdentifierPauseResumeSpeech),    AxTextCtrlAdapter::OnPauseResumeSpeech)

END_EVENT_TABLE()

AxTextCtrlAdapter::~AxTextCtrlAdapter()
{
}

void AxTextCtrlAdapter::Init()
{
    m_textCtrl = NULL;
}

// Override to return the name of the control.
wxString AxTextCtrlAdapter::GetName()
{
    wxString text;
    if (m_textCtrl)
    {
        if (m_textCtrl->GetWindowStyle() & wxTE_MULTILINE)
            text << _("multi-line text");
        else
            text << _("text");
    }
    return text;
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxTextCtrlAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (m_textCtrl)
    {
        wxString label = GetLabel();
        if (!label.IsEmpty())
            text << label << wxT(".");

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            text << wxT(" ") << content;
            AddStop(text);
        }
    }
    if (text.IsEmpty())
        text << _("empty");
    if (!m_textCtrl->IsEnabled())
        text << wxT(". ") << _(" not available.");
    if (!m_textCtrl->IsEditable())
        text << wxT(". ") << _(" read only.");
    return text;
}

// Get content
wxString AxTextCtrlAdapter::GetContent(AxContentLevel level)
{
    wxString text;
    if (m_textCtrl)
    {
        if (m_textCtrl->GetWindowStyle() & wxTE_MULTILINE)
        {
            wxString value = m_textCtrl->GetValue();
            if (value.Length() > 1000 && level == AxContentSummary)
            {
                value = value.Left(1000);
                value << wxT(". ");
                value << wxT("(remaining text omitted)");
            }
            text << value;
        }
        else
            text << m_textCtrl->GetValue();
    }
    return text;
}

// Read the current sentence.
bool AxTextCtrlAdapter::ReadCurrentSentence()
{
    long pos = FindNextSentencePosition(m_textCtrl->GetInsertionPoint(), 0);
    if (pos != -1)
    {
        long from, to;
        wxString value;
        if (GetSentence(pos, from, to, value) && !value.IsEmpty())
        {
            StopSpeech(AxSpeechAllChannels);
            Say(AxSpeechContentChannel, value);
            return true;
        }
    }
    return false;
}

// Read the next sentence.
bool AxTextCtrlAdapter::ReadNextSentence()
{
    long pos = FindNextSentencePosition(m_textCtrl->GetInsertionPoint(), 1);
    if (pos != -1)
    {
        long from, to;
        wxString value;
        if (GetSentence(pos, from, to, value) && !value.IsEmpty())
        {
            m_textCtrl->SetInsertionPoint(pos);

            StopSpeech(AxSpeechAllChannels);
            Say(AxSpeechContentChannel, value);
            return true;
        }
    }
    return false;
}

// Read the previous sentence.
bool AxTextCtrlAdapter::ReadPreviousSentence()
{
    long pos = FindNextSentencePosition(m_textCtrl->GetInsertionPoint(), -1);
    if (pos != -1)
    {
        long from, to;
        wxString value;
        if (GetSentence(pos, from, to, value) && !value.IsEmpty())
        {
            m_textCtrl->SetInsertionPoint(pos);

            StopSpeech(AxSpeechAllChannels);
            Say(AxSpeechContentChannel, value);
            return true;
        }
    }
    return false;
}

// Read the current paragraph.
bool AxTextCtrlAdapter::ReadCurrentParagraph()
{
    long pos = FindNextParagraphPosition(m_textCtrl->GetInsertionPoint(), 0);
    if (pos != -1)
    {
        long from, to;
        wxString value;
        if (GetParagraph(pos, from, to, value) && !value.IsEmpty())
        {
            StopSpeech(AxSpeechAllChannels);
            Say(AxSpeechContentChannel, value);
            return true;
        }
    }
    return false;
}

// Read the previous paragraph.
bool AxTextCtrlAdapter::ReadPreviousParagraph()
{
    long pos = FindNextParagraphPosition(m_textCtrl->GetInsertionPoint(), -1);
    if (pos != -1)
    {
        long from, to;
        wxString value;
        if (GetParagraph(pos, from, to, value) && !value.IsEmpty())
        {
            m_textCtrl->SetInsertionPoint(pos);

            StopSpeech(AxSpeechAllChannels);
            Say(AxSpeechContentChannel, value);
            return true;
        }
    }
    return false;
}

// Read the next paragraph.
bool AxTextCtrlAdapter::ReadNextParagraph()
{
    long pos = FindNextParagraphPosition(m_textCtrl->GetInsertionPoint(), 1);
    if (pos != -1)
    {
        long from, to;
        wxString value;
        if (GetParagraph(pos, from, to, value) && !value.IsEmpty())
        {
            m_textCtrl->SetInsertionPoint(pos);

            StopSpeech(AxSpeechAllChannels);
            Say(AxSpeechContentChannel, value);
            return true;
        }
    }
    return false;
}

// Read the current word.
bool AxTextCtrlAdapter::ReadCurrentWord()
{
    long pos = FindNextWordPosition(m_textCtrl->GetInsertionPoint(), 0);
    if (pos != -1)
    {
        long from, to;
        wxString value;
        if (GetWord(pos, from, to, value) && !value.IsEmpty())
        {
            StopSpeech(AxSpeechAllChannels);
            Say(AxSpeechContentChannel, value);
            return true;
        }
    }
    return false;
}

// Read the previous word.
bool AxTextCtrlAdapter::ReadPreviousWord()
{
    long pos = FindNextWordPosition(m_textCtrl->GetInsertionPoint(), -1);
    if (pos != -1)
    {
        long from, to;
        wxString value;
        if (GetWord(pos, from, to, value) && !value.IsEmpty())
        {
            m_textCtrl->SetInsertionPoint(pos);
            StopSpeech(AxSpeechAllChannels);
            Say(AxSpeechContentChannel, value);
            return true;
        }
    }
    return false;
}

// Read the next word.
bool AxTextCtrlAdapter::ReadNextWord()
{
    long pos = FindNextWordPosition(m_textCtrl->GetInsertionPoint(), 1);
    if (pos != -1)
    {
        long from, to;
        wxString value;
        if (GetWord(pos, from, to, value) && !value.IsEmpty())
        {
            m_textCtrl->SetInsertionPoint(pos);
            StopSpeech(AxSpeechAllChannels);
            Say(AxSpeechContentChannel, value);
            return true;
        }
    }
    return false;
}

// Spell the current word.
bool AxTextCtrlAdapter::SpellWord()
{
    // TODO
    return true;
}

// Read from the current position.
bool AxTextCtrlAdapter::ReadFromCurrentPosition()
{
    wxString value = m_textCtrl->GetRange(m_textCtrl->GetInsertionPoint(), m_textCtrl->GetLastPosition());
    if (!value.IsEmpty())
    {
        StopSpeech(AxSpeechAllChannels);
        Say(AxSpeechContentChannel, value);
        return true;
    }
    else
        return false;
}

// Read from the start.
bool AxTextCtrlAdapter::ReadFromStart()
{
    wxString value = m_textCtrl->GetValue();
    if (!value.IsEmpty())
    {
        StopSpeech(AxSpeechAllChannels);
        Say(AxSpeechContentChannel, value);
        return true;
    }
    else
        return false;
}

// Describe current context.
bool AxTextCtrlAdapter::DescribeContext()
{
    long x = 0, y = 0;

    bool isSingleLine = (((m_textCtrl->GetWindowStyle() & wxTE_MULTILINE) == 0) || m_textCtrl->IsKindOf(CLASSINFO(wxSearchCtrl)));

    m_textCtrl->PositionToXY(m_textCtrl->GetInsertionPoint(), & x, & y);
    x ++;
    y ++;

    wxString text;
    if (isSingleLine)
    {
        wxString value = m_textCtrl->GetValue();
        if (value.IsEmpty())
            value = _("empty");
        text = wxString::Format(_("Position %d. Text is %s"), x, value.c_str());
        AddStop(text);
    }
    else
        text = wxString::Format(_("Line %d, position %d."), y, x);

    long from, to;
    wxString currentWord;
    long pos = FindNextWordPosition(m_textCtrl->GetInsertionPoint(), 0);
    if (pos != -1)
    {
        if (GetWord(pos, from, to, currentWord) && !currentWord.IsEmpty())
            text += wxString::Format(_(" Current word is: %s"), currentWord.c_str());
    }
    else
    {
        pos = FindNextWordPosition(m_textCtrl->GetInsertionPoint(), 1);
        if (GetWord(pos, from, to, currentWord) && !currentWord.IsEmpty())
            text += wxString::Format(_(" Next word is: %s"), currentWord.c_str());
    }

    if (!text.IsEmpty())
    {
        StopSpeech(AxSpeechAllChannels);
        Say(AxSpeechContentChannel, text, AxSpeechFlagsPurge, AxSpeechVerbosityLowest);
    }

    return true;
}

bool AxTextCtrlAdapter::GetWord(long currentPos, long& from, long& to, wxString& value)
{
    long lastPos = m_textCtrl->GetLastPosition();
    long i = currentPos;
    
    // First skip whitespace
    EatWhitespace(i);

    if (i <= lastPos)
    {
        from = i;

        value = wxEmptyString;

        // Find next whitespace
        while (i <= lastPos)
        {
            wxChar ch = GetChar(i);
            if (ch == (wxChar) 0 || IsWhitespace(ch))
                break;
            else
            {
                value += ch;
                i ++;
            }
        }

        to = i;

        return true;
    }
    else
        return false;
}

bool AxTextCtrlAdapter::GetSentence(long currentPos, long& from, long& to, wxString& value)
{
    long lastPos = m_textCtrl->GetLastPosition();
    long i = currentPos;
    
    // First skip whitespace
    EatWhitespace(i);

    if (i <= lastPos)
    {
        from = i;

        value = wxEmptyString;

        // Find next end of sentence
        while (i <= lastPos)
        {
            wxChar ch = GetChar(i);
            if (ch == (wxChar) 0 || IsSentenceEnd(ch))
            {
                if (ch != (wxChar) 0)
                    value += ch;
                break;
            }
            else
            {
                value += ch;
                i ++;
            }
        }

        if (i > lastPos)
            i = lastPos;

        to = i;

        return true;
    }
    else
        return false;
}

bool AxTextCtrlAdapter::GetParagraph(long currentPos, long& from, long& to, wxString& value)
{
    long lastPos = m_textCtrl->GetLastPosition();
    long i = currentPos;
    
    // First skip whitespace
    EatWhitespace(i);

    if (i <= lastPos)
    {
        from = i;

        value = wxEmptyString;

        // Find next end of sentence
        while (i <= lastPos)
        {
            wxChar ch = GetChar(i);
            if (ch == wxT('\n'))
            {
                break;
            }
            else
            {
                value += ch;
                i ++;
            }
        }

        if (i > lastPos)
            i = lastPos;

        to = i;

        return true;
    }
    else
        return false;
}

long AxTextCtrlAdapter::FindNextWordPosition(long currentPos, int direction)
{
    long lastPos = m_textCtrl->GetLastPosition();
    if (direction == 0)
    {
        // This word
        long i = currentPos;

        // Skip any whitespace until next word
        while (i <= lastPos)
        {
            wxChar ch = GetChar(i);
            if (ch != (wxChar) 0 && !IsWhitespace(ch))
            {
                i --;
                break;
            }
            else
                i ++;
        }
        if (i > lastPos)
            return -1;

        // Skip non-whitespace until beginning of word
        while (i >= 0)
        {
            wxChar ch = GetChar(i);
            if (ch == (wxChar) 0 || IsWhitespace(ch))
            {
                i ++;
                break;
            }
            else
                i --;
        }

        if (i < 0)
            i = 0;
        return i;
    }
    else if (direction > 0)
    {
        long i = currentPos;

        // First skip current text to space
        while (i <= lastPos)
        {
            wxChar ch = GetChar(i);
            if (ch == (wxChar) 0 || IsWhitespace(ch))
                break;
            else
                i ++;
        }
        if (i > lastPos)
            return -1;

        // Now skip whitespace until next word
        while (i <= lastPos)
        {
            wxChar ch = GetChar(i);
            if (ch != (wxChar) 0 && !IsWhitespace(ch))
            {
                //i --;
                break;
            }
            else
                i ++;
        }

        if (i > lastPos)
            return -1;
        else
            return i;
    }
    else
    {
        long i = currentPos-1;

        // First skip whitespace
        while (i >= 0)
        {
            wxChar ch = GetChar(i);
            if (ch != (wxChar) 0 && !IsWhitespace(ch))
                break;
            else
                i --;
        }
        if (i < 0)
            return -1;

        // Now skip non-whitespace until beginning of word
        while (i >= 0)
        {
            wxChar ch = GetChar(i);
            if (ch == (wxChar) 0 || IsWhitespace(ch))
            {
                i ++;
                break;
            }
            else
                i --;
        }

        if (i < 0)
            i = 0;
        return i;
    }
}

long AxTextCtrlAdapter::FindNextSentencePosition(long currentPos, int direction)
{
    long lastPos = m_textCtrl->GetLastPosition();
    if (direction == 0)
    {
        long i = currentPos;

        while (i >= 0)
        {
            wxChar ch = GetChar(i);
            if (IsSentenceEnd(ch))
            {
                i ++;
                EatWhitespace(i);
                break;
            }
            else
                i --;
        }
        if (i > lastPos)
            return -1;

        i = wxMax(0, i);

        return i;
    }
    else if (direction > 0)
    {
        long i = currentPos;

        while (i <= lastPos)
        {
            wxChar ch = GetChar(i);
            if (IsSentenceEnd(ch))
            {
                i ++;
                EatWhitespace(i);
                break;
            }
            else
                i ++;
        }
        if (i > lastPos)
            return -1;

        return i;
    }
    else if (direction < 0)
    {
        long i = currentPos;
        int count = 0;

        while (i >= 0)
        {
            wxChar ch = GetChar(i);
            if (IsSentenceEnd(ch))
            {
                count ++;

                if (count == 2)
                {
                    i ++;
                    EatWhitespace(i);
                    break;
                }
                else
                    i --;
            }
            else
                i --;
        }
        i = wxMax(0, i);

        return i;
    }

    return -1;
}

long AxTextCtrlAdapter::FindNextParagraphPosition(long currentPos, int direction)
{
    long lastPos = m_textCtrl->GetLastPosition();
    if (direction == 0)
    {
        long i = currentPos;

        while (i >= 0)
        {
            wxChar ch = GetChar(i);
            if (ch == wxT('\n'))
            {
                i ++;
                break;
            }
            else
                i --;
        }
        i = wxMax(0, i);

        return i;
    }
    else if (direction > 0)
    {
        long i = currentPos;

        while (i <= lastPos)
        {
            wxChar ch = GetChar(i);
            if (ch == wxT('\n'))
            {
                i ++;
                break;
            }
            else
                i ++;
        }
        if (i > lastPos)
            return -1;

        return i;
    }
    else if (direction < 0)
    {
        long i = currentPos;
        int count = 0;

        while (i >= 0)
        {
            wxChar ch = GetChar(i);
            if (ch == wxT('\n'))
            {
                count ++;

                if (count == 2)
                {
                    i ++;
                    break;
                }
                else
                    i --;
            }
            else
                i --;
        }
        i = wxMax(0, i);

        return i;
    }

    return -1;
}

bool AxTextCtrlAdapter::EatWhitespace(long& pos)
{
    long lastPos = m_textCtrl->GetLastPosition();
    while (pos <= lastPos)
    {
        wxChar ch = GetChar(pos);
        if (ch != (wxChar)0 && IsWhitespace(ch))
            pos ++;
        else
        {
            break;
        }
    }
    return pos <= lastPos;
}

wxChar AxTextCtrlAdapter::GetChar(long pos)
{
    //long ip = m_textCtrl->GetInsertionPosition();
    if (pos == m_textCtrl->GetLastPosition())
        return (wxChar) 0;

    wxString str = m_textCtrl->GetRange(pos, pos+1);
    if (str.Length() == 1)
        return str[0];
    else
        return (wxChar) 0;
}

void AxTextCtrlAdapter::OnReadCurrentWord(wxCommandEvent& WXUNUSED(event))
{
    ReadCurrentWord();
}

void AxTextCtrlAdapter::OnReadNextWord(wxCommandEvent& WXUNUSED(event))
{
    ReadNextWord();
}

void AxTextCtrlAdapter::OnReadPreviousWord(wxCommandEvent& WXUNUSED(event))
{
    ReadPreviousWord();
}

void AxTextCtrlAdapter::OnReadCurrentSentence(wxCommandEvent& WXUNUSED(event))
{
    ReadCurrentSentence();
}

void AxTextCtrlAdapter::OnReadNextSentence(wxCommandEvent& WXUNUSED(event))
{
    ReadNextSentence();
}

void AxTextCtrlAdapter::OnReadPreviousSentence(wxCommandEvent& WXUNUSED(event))
{
    ReadPreviousSentence();
}

void AxTextCtrlAdapter::OnReadCurrentParagraph(wxCommandEvent& WXUNUSED(event))
{
    ReadCurrentParagraph();
}

void AxTextCtrlAdapter::OnReadNextParagraph(wxCommandEvent& WXUNUSED(event))
{
    ReadNextParagraph();
}

void AxTextCtrlAdapter::OnReadPreviousParagraph(wxCommandEvent& WXUNUSED(event))
{
    ReadPreviousParagraph();
}

void AxTextCtrlAdapter::OnReadFromHere(wxCommandEvent& WXUNUSED(event))
{
    ReadFromCurrentPosition();
}

void AxTextCtrlAdapter::OnReadAll(wxCommandEvent& WXUNUSED(event))
{
    ReadFromStart();
}

void AxTextCtrlAdapter::OnDescribeContext(wxCommandEvent& WXUNUSED(event))
{
    DescribeContext();
}

/// Update the appearance - text size, background, etc.
bool AxTextCtrlAdapter::UpdateAppearance(int hints)
{
    if (AxViewManager::Get() && GetWindow())
    {
        if (hints & AxVisualSettingTextSize)
        {
            wxFont font(GetWindow()->GetFont());
            font.SetPointSize(AxViewManager::Get()->GetVisualSettings().GetBestTextSize());
            GetWindow()->SetFont(font);

            if (GetTextCtrl() && (GetTextCtrl()->GetWindowStyle() & wxTE_MULTILINE) == 0)
            {
                wxSize sz = GetTextCtrl()->GetSize();
                wxSize bestSize = GetTextCtrl()->GetBestSize();
                GetTextCtrl()->SetSize(wxSize(sz.x, bestSize.y));
            }
        }

        if (hints & AxVisualSettingTextColour)
        {
            GetWindow()->SetForegroundColour(AxViewManager::Get()->GetVisualSettings().GetBestTextColour());
        }

        if (hints & AxVisualSettingWindowColour)
        {
            GetWindow()->SetBackgroundColour(AxViewManager::Get()->GetVisualSettings().GetBestWindowColour());
        }
    }

    return true;
}

IMPLEMENT_DYNAMIC_CLASS(AxSearchCtrlAdapter, AxTextCtrlAdapter)

BEGIN_EVENT_TABLE(AxSearchCtrlAdapter, AxTextCtrlAdapter)
    EVT_TEXT_ENTER(wxID_ANY, AxSearchCtrlAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxSearchCtrlAdapter::GetName()
{
   return _("search control");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxSearchCtrlAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetSearchCtrl())
    {
        wxString label = GetLabel();
        if (!label.IsEmpty())
            text << label;

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ") << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxSearchCtrlAdapter::GetContent(AxContentLevel WXUNUSED(level))
{
    wxSearchCtrl* searchCtrl = GetSearchCtrl();
    wxString text;

    if (searchCtrl)
    {
        wxString value = searchCtrl->GetValue();
        if (value.IsEmpty())
            value = _("empty");
        text << wxString::Format(_("The search string is %s."), value.c_str());
    }

    return text;
}

void AxSearchCtrlAdapter::OnCommand(wxCommandEvent& event)
{
    event.Skip();

    if (!GetSearchCtrl() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString value = GetSearchCtrl()->GetValue();
    if (value.IsEmpty())
        value = _("empty");
    wxString text(wxString::Format(_("activating search string %s."), value.c_str()));
    Say(text);
}

wxSearchCtrl* AxSearchCtrlAdapter::GetSearchCtrl() const
{
    wxSearchCtrl* searchCtrl = wxDynamicCast(GetWindow(), wxSearchCtrl);
    if (!searchCtrl)
        searchCtrl = wxDynamicCast(GetWindow()->GetParent(), wxSearchCtrl);
    return searchCtrl;
}

wxWindow* AxSearchCtrlAdapter::GetMainControl() const
{
    return GetSearchCtrl();
}

// Adaptation handlers

IMPLEMENT_DYNAMIC_CLASS( AxTextCtrlVoiceAdaptationHandler, AxVoiceAdaptationHandler)

bool AxTextCtrlVoiceAdaptationHandler::CanHandleWindow(wxWindow* win)
{
    wxSearchCtrl* searchCtrl = wxDynamicCast(win, wxSearchCtrl);
    wxTextCtrlBase* textCtrl = wxDynamicCast(win, wxTextCtrlBase);
    return (textCtrl || searchCtrl);
}

AxAdapter* AxTextCtrlVoiceAdaptationHandler::CreateEventHandler(wxWindow* win)
{
    wxSearchCtrl* searchCtrl = wxDynamicCast(win, wxSearchCtrl);
    wxTextCtrlBase* textCtrl = wxDynamicCast(win, wxTextCtrlBase);

    if (searchCtrl)
        return new AxSearchCtrlAdapter;
    else if (textCtrl)
    {
        AxTextCtrlAdapter* handler = new AxTextCtrlAdapter;
        handler->SetTextCtrl(textCtrl);
        return handler;
    }
    else
        return NULL;
}

// Override to adapt e.g. a composite control.
bool AxTextCtrlVoiceAdaptationHandler::Adapt(wxWindow* window, AxSpeech* speech, wxShortcutManager* shortcuts, wxArrayInt& ids)
{
    if (!AxVoiceAdaptationHandler::Adapt(window, speech, shortcuts, ids))
        return false;

    wxSearchCtrl* searchCtrl = wxDynamicCast(window, wxSearchCtrl);
    if (searchCtrl)
    {
        wxTextCtrl* textCtrl = (wxTextCtrl*) AxUIUtilities::FindWindowByClass(window, CLASSINFO(wxTextCtrl));
        if (textCtrl)
        {
            AxSearchCtrlAdapter* adapter = new AxSearchCtrlAdapter;

            adapter->SetWindow(textCtrl);
            adapter->SetSpeech(speech);
            adapter->SetShortcutManager(shortcuts);
            adapter->SetTextCtrl(textCtrl);

            ids.Add(textCtrl->GetId());
            textCtrl->PushEventHandler(adapter);
        }
        
        return true;
    }
    return true;
}

// Override to unadapt e.g. a composite control.
bool AxTextCtrlVoiceAdaptationHandler::Unadapt(wxWindow* window, wxArrayInt& ids)
{
    if (!AxVoiceAdaptationHandler::Unadapt(window, ids))
        return false;

    wxSearchCtrl* searchCtrl = wxDynamicCast(window, wxSearchCtrl);
    if (searchCtrl)
    {
        wxTextCtrl* textCtrl = (wxTextCtrl*) AxUIUtilities::FindWindowByClass(window, CLASSINFO(wxTextCtrl));
        if (textCtrl)
        {
            AxVoiceAdaptationHandler::Unadapt(textCtrl, ids);
        }
    }

    return true;
}

/// Override to prevent recursive adaptation, e.g. into a composite.
bool AxTextCtrlVoiceAdaptationHandler::CanRecursivelyAdapt(wxWindow* WXUNUSED(window)) const
{
    return false;
}
